﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#ifndef __ROUTINE_IMP_H__
#define __ROUTINE_IMP_H__

#pragma once

#include "IAuthorRoutine.h"
#include "authorsession.h"
#include "routinedef.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAuthorRoutine :
// 1. 异步事件队列维护
// 2. 会话状态+id管理
// 3. 业务区签证流程
class CAuthorRoutine : public IAuthorRoutine
{
    DECLARE_RTTI_CREATE( CAuthorRoutine )
public:
    enum QUEUE_TIME
    {
        QUEUE_TIME_ID   = SESSION_ID_AUTHOR + 1,
#ifdef __RUNTIME_DEBUG__
        QUEUE_TIME_TICK = 1000,
#else
        QUEUE_TIME_TICK = 500,
#endif
    };
public:
    CAuthorRoutine(void);
    virtual ~CAuthorRoutine(void);
    // CEventHandler
    virtual UInt OnHandle(Int nEvent, uintptr_t utData, ULLong ullParam) OVERRIDE;
    virtual UInt OnHandle(Int nEvent, CEventBase&, ULLong) OVERRIDE;
    virtual UInt OnHandle(Int nEvent, CStream& Stream, ULLong ullParam) OVERRIDE;
    virtual UInt OnHandle(Int nEvent, UInt uCount) OVERRIDE;
    // ICommonRoutine
    virtual bool Start(ICommonServer* pServer) OVERRIDE;
    virtual bool Pause(bool bPause = true) OVERRIDE;
    virtual bool Update(void) OVERRIDE;
    virtual void Stop(void) OVERRIDE;

    virtual UInt     GetThread(void) OVERRIDE;//
    virtual Socket_t GetSocket(UInt uType, UInt uId) OVERRIDE;//
    virtual bool     SetTimeout(UInt uTimeout = 0) OVERRIDE;

    virtual bool AddQueue(Int nEvent, uintptr_t utData = 0, ULLong ullParam = 0, QUEUE_TYPE eType = QUEUE_TYPE_NONE) OVERRIDE;
    virtual bool AddQueue(Int nEvent, CEventBase& EventRef, ULLong ullParam = 0, QUEUE_TYPE eType = QUEUE_TYPE_REFCOUNT) OVERRIDE;
    virtual bool AddQueue(Int nEvent, CStream& Stream, ULLong ullParam = 0) OVERRIDE;
private:
    bool  LoadExtConfig(bool bUpdate = false);
    bool  LoadExtBusiness(CKVNode& Root);
private:
    bool  OnExternalBondWait(CEventBase& EventRef, ULLong ullParam);
    bool  OnExternalBond(CEventBase& EventRef, ULLong ullParam);
    bool  OnExternalLogin(CEventBase& EventRef, ULLong ullParam);

    bool  OnExternalCustom(CEventBase& EventRef, ULLong ullParam);
    bool  OnInternalCustom(CEventBase& EventRef, ULLong ullParam);
    //-------------------------------------------------------------
    bool  OnAuthorDBMaintainAck(CEventBase& EventRef, ULLong ullParam);
    //-------------------------------------------------------------
    bool  OnMasterVisa(ULLong ullParam);
    bool  OnMasterVisaManual(ULLong ullMaster, ULLong ullParam);
    bool  OnMasterVisaAck(CEventBase& EventRef, ULLong ullParam);
    bool  OnMasterCheckAck(CEventBase& EventRef, ULLong ullParam);
    bool  OnMasterCompleteAck(CEventBase& EventRef, ULLong ullParam);
    //-------------------------------------------------------------
    bool  CheckVisaQueue(void);
    bool  CheckSessionStatus(AUTHOR_SESSION_MAP& CheckSessions);
    bool  HandleSessionStatus(AUTHOR_SESSION_MAP& CheckSessions);
    //-------------------------------------------------------------
    bool  GetSession(ULLong ullParam, CAuthorSessionPtr& SessionPtr);
    LLong IncrementUpdate(LLong llUpdate, ULLong ullParam); // / 业务网关有一样的功能

    bool  CalcVisaQueue(CStream& Stream, UInt uMode);
    bool  InVisaQueue(ULLong ullAuthor, ULLong ullMaster = 0);
    //-------------------------------------------------------------
    template <typename NETStream>
    UInt  CallSessionBusiness(NETStream* pStream, ULLong ullParam, CAuthorSessionPtr& SessionPtr);

    template <typename NETStream>
    bool  CheckSessionIncrement(NETStream* pStream, UInt uCurStatus, UInt uSetStatus, CAuthorSessionPtr& SessionPtr);
    //-------------------------------------------------------------
    bool  ResetAck(CPAKHeadBase* pPAKHead, UEVENT uEvent, UACK uAck = (UACK)ERRORD_OKAY, UTYPE uType = (UTYPE)PAKT_AUTHOR);
private:
    ICommonServer*        m_pServer;         ///< 框架例程处理对应的服务器
    PULLong               m_pKeyPtr;
    time_t                m_tExtConfig;      ///< 扩展配置文件修改时间
    Int                   m_nTime;           ///< update()倍数次进行配置文件修改检测
    Int                   m_nUpdate;         ///<
    UInt                  m_uThreads;        ///< [1, cores]
    UInt                  m_uTimeout;        ///< 异步事件队列工作线程超时检测的时间, 单位[S];
    //-------------------------------------------------------------
    UInt                  m_uCenterId;
    UInt                  m_uAuthorDBId;
    UInt                  m_uMaintainBegin;  // 授权数据维护开始时间
    UInt                  m_uMaintainEnd;    // 授权数据维护结束时间
    // 强制升级版本的主次号, 均为0表示不需要强制升级
    UInt                  m_uUpdateMajor;
    UInt                  m_uUpdateMinor;

    UInt                  m_uVisaMode; // 0---自动; 1---手动
    UInt                  m_uVisaTick; // 更新时间
    UInt                  m_uVisaQueue;
    UInt                  m_uVisaComplete;

    UInt                  m_uMasterTry;
    UInt                  m_uTimeoutTick;
    UInt                  m_uTimeoutMin;
    UInt                  m_uTimeoutMax;
    UInt                  m_uTimeoutIdle;
    UInt                  m_uTimeoutWait;

    UInt                  m_uCompleteMode; // 0---定时断开, 1---维持
    UInt                  m_uCompleteTime;

    AUTHOR_SESSION_MAP    m_SessionMap;
    AUTHOR_INDEX_MAP      m_SessionIndex; // 
    CSyncLock             m_SessionLock;

    AUTHOR_VISA_QUEUE     m_VisaQueue;
    CSyncLock             m_VisaLock;
    //-------------------------------------------------------------
    CEventQueuePtr        m_EventQueuePtr;   ///< 事件队列
    CCommonRoutinePtr     m_BusinessPtr;     ///< 和产品业务相关的
};

INLINE CAuthorRoutine::CAuthorRoutine(void)
: m_pServer(nullptr)
, m_pKeyPtr(nullptr)
, m_tExtConfig(0)
, m_nTime(DATAD_UPDATE)
, m_nUpdate(0)
, m_uThreads(1)
, m_uTimeout(0)
, m_uCenterId(0)
, m_uAuthorDBId(0)
, m_uMaintainBegin(0)
, m_uMaintainEnd(0)
, m_uUpdateMajor(0)
, m_uUpdateMinor(0)
, m_uVisaMode(VISA_MODE_NONE)
, m_uVisaTick(QUEUE_TIME_TICK)
, m_uVisaQueue(0)
, m_uVisaComplete(0)
, m_uMasterTry(SESSION_TIME_MASTER_TRY)
, m_uTimeoutTick(SESSION_TIME_OUT_TICK)
, m_uTimeoutMin(SESSION_TIME_OUT_MID)
, m_uTimeoutMax(SESSION_TIME_OUT_MAX)
, m_uTimeoutIdle(SESSION_TIME_OUT_IDLE)
, m_uTimeoutWait(SESSION_TIME_OUT_WAIT)
, m_uCompleteMode(COMPLETE_MODE_NONE)
, m_uCompleteTime(SESSION_TIME_COMPLETE)
{
    DEV_DEBUG(TF(" + %s new instance at %p"), GetRTTI().GetName(), this);
}

INLINE CAuthorRoutine::~CAuthorRoutine(void)
{
    DEV_DEBUG(TF(" - %s delete instance at %p"), GetRTTI().GetName(), this);
}

INLINE UInt CAuthorRoutine::GetThread(void)
{
    return m_uThreads;
}

INLINE Socket_t CAuthorRoutine::GetSocket(UInt, UInt)
{
    return 0;
}

INLINE bool CAuthorRoutine::GetSession(ULLong ullParam, CAuthorSessionPtr& SessionPtr)
{
    CSyncLockWaitScope scope(m_SessionLock);
    PINDEX index = m_SessionMap.FindIndex(ullParam);
    if (index != nullptr)
    {
        SessionPtr = m_SessionMap[index];
    }
    return (SessionPtr != nullptr);
}

INLINE LLong CAuthorRoutine::IncrementUpdate(LLong llUpdate, ULLong ullParam)
{
    ullParam  = *(m_pKeyPtr + ullParam % 2047);
    ullParam += *(m_pKeyPtr + llUpdate % 2047);
    return (llUpdate + 200 + (LLong)(ullParam & 0x03FF));
}

INLINE bool CAuthorRoutine::CalcVisaQueue(CStream& Stream, UInt uMode)
{
    if (uMode == VISA_MODE_NONE)
    {
        UInt uQueue = (UInt)m_VisaQueue.GetSize();
        UInt uWait  = (uQueue / VISA_QUEUE_LIMIT);
        uWait = uWait * m_uVisaTick + m_uVisaTick;
        Stream << (UInt)SESSION_LOGIN_VISA << uQueue << uWait;
    }
    else
    {
        Stream << (UInt)SESSION_LOGIN_ACK << m_uTimeoutIdle << m_uTimeoutWait;
    }
    return true;
}

INLINE bool CAuthorRoutine::InVisaQueue(ULLong ullAuthor, ULLong ullMaster)
{
    if (m_uVisaQueue <= (CAtomics::CompareExchange<UInt>(&m_uVisaComplete, 0, 0) + VISA_QUEUE_DEFAULT))
    {
        CAtomics::Increment<UInt>(&m_uVisaQueue);
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[授权网关例程]!!!!!!!!!!!!!!!!!!!!!快速签证-签证排队申请数量=%d, 完成数量=%d"), m_uVisaQueue, m_uVisaComplete);
#endif
        return m_EventQueuePtr->Add(PAKE_AUTHOR_VISA, ullMaster, ullAuthor);
    }
    {
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[授权网关例程]!!!!!!!!!!!!!!!!!!!!!签证排队-签证排队数量=%d, 申请数量=%d, 完成数量=%d"), m_VisaQueue.GetSize() + 1, m_uVisaQueue, m_uVisaComplete);
#endif
        CSyncLockWaitScope scope(m_VisaLock);
        AUTHOR_VISA Visa;
        Visa.ullAuthor = ullAuthor;
        Visa.ullMaster = ullMaster;
        return m_VisaQueue.In(Visa);
    }
}

template <typename NETStream>
INLINE UInt CAuthorRoutine::CallSessionBusiness(NETStream* pStream, ULLong ullParam, CAuthorSessionPtr& SessionPtr)
{
    UInt uRet = SESSION_STATUS_DEFAULT;
    if (m_BusinessPtr != nullptr)
    {
        // !!!业务例程 : 自己发送到外网端注意sessionid+PAKT_TYPE确保正确
        uRet = m_BusinessPtr->OnHandle((Int)pStream->GetEvent(), *pStream, ullParam);
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[授权网关例程]%p调用业务例程进行处理返回=%X"), ullParam, uRet);
    }
    else
    {
        DEV_DUMP(TF("[授权网关例程]%p调用业务例程时无实例, 直接走默认流程"), ullParam);
#endif
    }
    if (uRet != (UInt)RET_ERROR) // 错误关闭连接
    {
        switch (uRet)
        {
        case SESSION_STATUS_DEFAULT:
            {
#ifdef __DUMP_STEP_DETAIL__
                DEV_DUMP(TF("[授权网关例程]%p调用业务例程返回默认处理, 由框架例程完成默认流程"), ullParam);
#endif
            }
            break;
        case SESSION_STATUS_HANDLE:
            {
                SessionPtr->IncrTryCount();
#ifdef __DUMP_STEP_DETAIL__
                DEV_DUMP(TF("[授权网关例程]%p调用业务例程返回已经处理状态, 保存现有状态不变"), ullParam);
#endif
            }
            break;
        case SESSION_STATUS_BOND:
            {
#ifdef __DUMP_STEP_DETAIL__
                DEV_DUMP(TF("[授权网关例程]%p调用业务例程返回切换到连接开始状态"), ullParam);
#endif
                if (SessionPtr->TrySetStatusGTOE(SESSION_STATUS_LOGINACK, SESSION_STATUS_BOND))
                {
                    SessionPtr->SetTarget(0);
                    SessionPtr->SetType(0);
                    SessionPtr->SetId(0);
                    SessionPtr->SetllId(0);

                    SessionPtr->SetUpdate(CPlatform::GetRunningTime());
                    SessionPtr->SetTimeId(SESSION_ID_IDLE);
                    SessionPtr->SetTryCount();
                    {
                        CSyncLockWaitScope scope(m_SessionLock);
                        if ((SessionPtr->GetIndex() != nullptr) && (m_SessionIndex.Find(ullParam) == nullptr))
                        {
                            m_SessionIndex.Add(ullParam, SessionPtr->GetIndex());
                        }
                    }
                    m_pServer->GetNetworkPtr()->SetAttr((Socket_t)ullParam, PAKT_EXTERNAL_READY);

                    CNETBondAck Ack;
                    CStream& Stream = Ack.Alloc(m_pServer->GetNetworkPtr());
                    Stream << (UInt)SESSION_BOND_LOGOUT << m_uAuthorDBId << m_uCenterId << m_uMaintainBegin << m_uMaintainEnd;
                    Ack.Send((Socket_t)ullParam, m_pServer->GetNetworkPtr());
                }
            }
            break;
        case SESSION_STATUS_LOGIN:
            {
#ifdef __DUMP_STEP_DETAIL__
                DEV_DUMP(TF("[授权网关例程]%p调用业务例程返回内部处理以后异步返回结果"), ullParam);
#endif
                if (SessionPtr->TrySetStatusLTOE(SESSION_STATUS_LOGINWAIT, SESSION_STATUS_LOGIN))
                {
                    SessionPtr->SetUpdate(CPlatform::GetRunningTime());
                    SessionPtr->SetTimeId(SESSION_ID_MAX);
                }
            }
            break;
        case SESSION_STATUS_LOGINACK:
            {
#ifdef __DUMP_STEP_DETAIL__
                DEV_DUMP(TF("[授权网关例程]%p调用业务例程返回验证完成, 回复外网端验证通过信息状态"));
#endif
                if (SessionPtr->TrySetStatusLTOE(SESSION_STATUS_LOGINWAIT, SESSION_STATUS_LOGINACK))
                {
                    CStream& StreamId = pStream->ReadStream(m_pServer->GetNetworkPtr());
                    SESSION_ID sid;
                    StreamId.Read(&sid, sizeof(SESSION_ID));

                    SessionPtr->SetVer(sid.uVer);
                    SessionPtr->SetTarget(sid.uTarget);
                    SessionPtr->SetType(sid.uType);
                    SessionPtr->SetId(sid.uId);
                    SessionPtr->SetllId(sid.ullId);

                    SessionPtr->SetUpdate(CPlatform::GetRunningTime());
                    SessionPtr->SetTimeId(SESSION_ID_IDLE);
                    SessionPtr->SetTryCount();
                    m_pServer->GetNetworkPtr()->SetAttr((Socket_t)ullParam, PAKT_EXTERNAL_AUTHOR);

                    CStream& Stream = pStream->WriteStream(m_pServer->GetNetworkPtr());
                    CalcVisaQueue(Stream, m_uVisaMode);

                    ResetAck(pStream, PAKE_AUTHOR_LOGINACK);
                    pStream->SetSessionId(ullParam);
                    pStream->Send((Socket_t)ullParam, m_pServer->GetNetworkPtr());

                    if (m_uVisaMode == VISA_MODE_NONE)
                    {
                        InVisaQueue(ullParam);
                    }
                }
            }
            break;
        case SESSION_STATUS_LOGINWAIT:
            {
#ifdef __DUMP_STEP_DETAIL__
                DEV_DUMP(TF("[授权网关例程]%p调用业务例程返回进入等待内部处理状态"), ullParam);
#endif
                if (SessionPtr->TrySetStatusLTOE(SESSION_STATUS_LOGINWAIT, SESSION_STATUS_LOGINWAIT))
                {
                    SessionPtr->SetUpdate(CPlatform::GetRunningTime());

                    ResetAck(pStream, PAKE_AUTHOR_LOGINACK, (UACK)ROUTINE_ERROR_ACK_TIMEOUT);
                    pStream->Send((Socket_t)ullParam, m_pServer->GetNetworkPtr());
                }
            }
            break;
        case SESSION_STATUS_SELECT: // 指定业务服务器+网关服务器
            {
#ifdef __DUMP_STEP_DETAIL__
                DEV_DUMP(TF("[授权网关例程]%p调用业务例程返回, 选择业务区服务器"));
#endif
                if ((m_uVisaMode == VISA_MODE_SELECT) && (SessionPtr->CheckStatus(SESSION_STATUS_LOGINACK)))
                {
                    SessionPtr->SetUpdate(CPlatform::GetRunningTime());
                    SessionPtr->SetTryCount();

                    ULLong ullMaster = 0;
                    CStream& StreamMaster = pStream->ReadStream(m_pServer->GetNetworkPtr());
                    StreamMaster >> ullMaster;

                    CStream& Stream = pStream->WriteStream(m_pServer->GetNetworkPtr());
                    CalcVisaQueue(Stream, VISA_MODE_NONE);

                    ResetAck(pStream, PAKE_AUTHOR_LOGINACK);
                    pStream->SetSessionId(ullParam);
                    pStream->Send((Socket_t)ullParam, m_pServer->GetNetworkPtr());

                    InVisaQueue(ullParam, ullMaster);
                }
            }
            break;
        default:
            {
            }
        }
    }
    else
    {
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[授权网关例程]%p调用业务例程返回异常, 业务例程关闭连接"), ullParam);
#endif
        //m_pServer->GetNetworkPtr()->Destroy((Socket_t)ullParam, false);
    }
    return uRet;
}

template <typename NETStream>
INLINE bool CAuthorRoutine::CheckSessionIncrement(NETStream* pStream, UInt uCurStatus, UInt uSetStatus, CAuthorSessionPtr& SessionPtr)
{
    if (GetSession(pStream->GetSessionId(), SessionPtr) == false)
    {
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[授权网关例程]%p检测连接和会话映射关系失败2"), pStream->GetSessionId());
#endif
        return false;
    }
    LLong llUpdate = 0;
    if (uCurStatus == SESSION_STATUS_COMPLETE) // 业务区流程最后未完成重新开始
    {
        llUpdate = SessionPtr->GetUpdate();
    }
    else
    {
        CStreamSeekScope scope(pStream->GetStream());
        pStream->GetStream() >> llUpdate;
    }
    BEGIN_SCOPE( llUpdate, 0 );
        if (SessionPtr->TrySetUpdate(llUpdate, IncrementUpdate(llUpdate, pStream->GetSessionId())) == false)
        {
#ifdef __DUMP_STEP_DETAIL__
            DEV_DUMP(TF("[授权网关例程]%p检测连接的会话2时间戳失败%llX---%llX"), pStream->GetSessionId(), SessionPtr->GetUpdate(), llUpdate);
#endif
            break;
        }
        if (SessionPtr->TrySetStatus(uCurStatus, uSetStatus) == false)
        {
#ifdef __DUMP_STEP_DETAIL__
            DEV_DUMP(TF("[授权网关例程]%p检测连接的会话2状态失败%d---%d"), pStream->GetSessionId(), SessionPtr->GetStatus(), uCurStatus);
#endif
            break;
        }
        return true;
    END_SCOPE();
    return false;
}

INLINE bool CAuthorRoutine::ResetAck(CPAKHeadBase* pPAKHead, UEVENT uEvent, UACK uAck, UTYPE uType)
{
    pPAKHead->ResetHead(uEvent, uType, uAck);
    return true;
}

#endif // __ROUTINE_IMP_H__
